using System.Collections.Generic;
using System.Data.Linq;
using System.Linq;
using Magiq.Support;
using Magiq.Support.Database;
using Magiq.Update;

namespace Magiq.Sql {
    public class MassUpdate<T> where T : class {
        private readonly IDependentTablesUpdateStrategy dependentTablesUpdateStrategy;
        private readonly Dictionary<string, TableUpdate<T>> tables = new Dictionary<string, TableUpdate<T>>();

        public MassUpdate(IQueryableUpdate<T> update, DataContext dataContext,
                          IDependentTablesUpdateStrategy dependentTablesUpdateStrategy) {
            this.dependentTablesUpdateStrategy = dependentTablesUpdateStrategy;
            Update = update;
            DataContext = dataContext;

            foreach (var propertySet in update.PropertySets) {
                var key = propertySet.ParentKey;

                TableUpdate<T> tableUpdate;
                if (!tables.TryGetValue(key, out tableUpdate)) {
                    tableUpdate = new TableUpdate<T>(update.Where);
                    tables.Add(key, tableUpdate);
                }
                tableUpdate.Set(propertySet);
            }
        }

        public IQueryableUpdate<T> Update { get; private set; }

        public int AmountOfTables {
            get { return tables.Count; }
        }

        public IEnumerable<TableUpdate<T>> Tables {
            get { return tables.Values; }
        }

        public DataContext DataContext { get; private set; }

        private int Execute(TableUpdate<T> tableUpdate) {
            var dataProvider = new SqlDatabaseAdapter(DataContext);
            var queryExecutor = new UpdateExecutor(dataProvider, tableUpdate.Where, tableUpdate.PropertySets);

            var values = queryExecutor.BaseSelectedValues;
            var sets = new string[values.Length];
            var columns = queryExecutor.Columns;

            for (var i = 0; i < values.Length; i++)
                sets[i] = columns[i] + "=" + values[i];

            var updateSql = "UPDATE " + queryExecutor.BaseTableName + " SET " + sets.Join(",") + " FROM " +
                            queryExecutor.BaseWhereClause;

            var value = queryExecutor.ExecuteNonQuery(updateSql);

            return value;
        }

        public int Execute() {
            if (AmountOfTables == 1)
                return Execute(Tables.Single());

            var resultFromNotInvalidatingUpdates =
                Tables.Where(x => !x.IsWhereInvalidation).Sum(x => Execute(x));

            var invalidatingUpdates = Tables.Where(x => x.IsWhereInvalidation).ToList();

            if (invalidatingUpdates.Count == 1)
                return resultFromNotInvalidatingUpdates + Execute(invalidatingUpdates.Single());

            if (invalidatingUpdates.Count > 1)
                return resultFromNotInvalidatingUpdates +
                       dependentTablesUpdateStrategy.Execute(this, invalidatingUpdates);

            return resultFromNotInvalidatingUpdates;
        }
    }
}